RDD - Parallelize
The simplest way to create a Resilient Distributed Dataset (RDD) is to parallelize an existing collection (like a Python list, tuple, dictionary, or set) that resides in the memory of your Driver program. This is achieved using the parallelize() method of the SparkContext.
This guide provides a detailed walkthrough of parallelize(), detailing how data is divided, and supplying complete, executable code examples.
1. What is parallelize()?
The parallelize() method takes a local Python iterable on the Driver and sends its elements to the worker nodes to form a distributed RDD.
graph TD
subgraph Driver["Driver Program (Master Node)"]
LC["Local Python List: [1, 2, 3, 4, 5, 6]"]
SC["SparkContext.parallelize(list, numSlices=3)"]
end
subgraph Cluster["Executors (Worker Nodes)"]
E1["Executor 1: Partition 1 [1, 2]"]
E2["Executor 2: Partition 2 [3, 4]"]
E3["Executor 3: Partition 3 [5, 6]"]
end
LC --> SC
SC -->|Send Partitions| E1
SC -->|Send Partitions| E2
SC -->|Send Partitions| E3
style Driver fill:#e1f5fe,stroke:#039be5,stroke-width:2px;
style Cluster fill:#efebe9,stroke:#8d6e63,stroke-width:2px;
When is it used?
- Testing and Prototyping: Ideal for writing unit tests or verifying logic on small test inputs.
- Small Lookup Lists: Turning a small list of filter values or IDs into an RDD to join against a larger distributed dataset.
- Education: Great for demonstrating Spark concepts without needing large external files.
Warning
Do not use parallelize() on extremely large datasets (e.g., millions of rows or gigabytes of memory) because the entire collection must fit into the Driver program's memory before distribution, which can easily cause the Driver to experience an Out Of Memory (OOM) error.
2. Understanding Partitions (numSlices)
When you parallelize a collection, Spark attempts to divide it into multiple chunks called partitions (or slices).
- Default Partitions: If you do not specify a partition count, Spark automatically assigns one based on your cluster configuration. In local mode (e.g.,
local[*]), Spark defaults to the number of CPU cores available on your machine. - Custom Partitions: You can explicitly pass the number of partitions as the second argument:
sc.parallelize(collection, numSlices=N).
3. PySpark Code Examples
A. Initializing the Spark Session
First, we must create a SparkSession and get the SparkContext (sc):
from pyspark.sql import SparkSession
# Initialize Spark Session in local mode using all available CPU cores
spark = SparkSession.builder \
.appName("Day01 RDD Parallelize") \
.master("local[*]") \
.getOrCreate()
# Retrieve SparkContext (the entry point for raw RDD APIs)
sc = spark.sparkContext
B. Parallelizing a Simple Numeric List
Let's convert a standard Python list of numbers into a distributed RDD and count the elements:
# 1. Define a local Python list in Driver memory
local_numbers = [10, 20, 30, 40, 50]
# 2. Parallelize the list to create an RDD
# Spark will automatically determine the partition count based on available cores
numbers_rdd = sc.parallelize(local_numbers)
# 3. Verify the type of the created RDD
print("RDD Type:", type(numbers_rdd))
# Output: RDD Type: <class 'pyspark.rdd.RDD'>
# 4. Perform a simple action to fetch data back to the driver
result_list = numbers_rdd.collect()
print("Collected Elements:", result_list)
# Output: Collected Elements: [10, 20, 30, 40, 50]
C. Controlling Partition Count and Inspecting Data Layout
Let's explicitly set the partition count and inspect how Spark distributes elements inside partitions using .glom():
Note
The .glom() transformation groups all elements within each individual partition into a list. This allows us to see exactly how Spark slices our collection.
# 1. Parallelize a list of 10 items into exactly 4 partitions
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
partitioned_rdd = sc.parallelize(data, numSlices=4)
# 2. Query the number of partitions
num_partitions = partitioned_rdd.getNumPartitions()
print(f"Number of Partitions: {num_partitions}")
# Output: Number of Partitions: 4
# 3. View how the elements are distributed among the 4 partitions
partitioned_data = partitioned_rdd.glom().collect()
print("Data distribution per partition:")
for index, partition in enumerate(partitioned_data):
print(f" Partition {index}: {partition}")
# Typical Output:
# Data distribution per partition:
# Partition 0: [1, 2]
# Partition 1: [3, 4, 5]
# Partition 2: [6, 7]
# Partition 3: [8, 9, 10]
D. Parallelizing Key-Value Pairs
You can parallelize a list of Python tuples to create a Key-Value RDD, which enables the use of specialized Pair functions:
# 1. List of key-value tuples representing (User, Score)
scores_data = [("Alice", 85), ("Bob", 92), ("Alice", 95), ("Charlie", 78)]
# 2. Parallelize into 2 partitions
scores_rdd = sc.parallelize(scores_data, numSlices=2)
# 3. Output partitions using glom
print("Key-Value Distribution:")
for idx, partition in enumerate(scores_rdd.glom().collect()):
print(f" Partition {idx}: {partition}")
# Output:
# Key-Value Distribution:
# Partition 0: [('Alice', 85), ('Bob', 92)]
# Partition 1: [('Alice', 95), ('Charlie', 78)]
E. Stopping the Spark Session
Always close the SparkSession at the end of your script to free system resources:
spark.stop()